#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>

#define FLAT_CLEANUP_FREQUENCY 			(8 << 1UL)
#define FLAT_CLEANUP_OLD_THRESHOLD 		(0 << 1UL)
/* 分别flat 和全局 flat */
/* 全局状态 */

/* 这个flatnode分配怎么处理? */
/* flat申请的时候会附带 flatnode? 而且可扩展 */

#define FLAT_GET_AGE(cur_age_active)		((cur_age_active) & (~0x1UL))
#define FLAT_INCR_AGE(cur_age_active)		((cur_age_active) + 0x2UL)
#define FLAT_PENDING				(0x1UL)
#define FLAT_ACTIVE				(0x1UL)
#define FLAT_IS_ACTIVE(age_active)		((age_active) & 0x1UL)
#define FLAT_GET_AGE_ACTIVE(age_active_flag)	((hword_t)(age_active_flag))
#define FLAT_GET_FLAG(age_active_flag)		((age_active_flag) >> HWORD_WIDTH)

typedef struct _flatnode
{
	struct{
		APDSFUTURE_DECL_NOFLAG
		/* 整个就是一个word, 写入的时候可以一起写入(同时更新age和打上戳), 换代时只需要修改前半部分 */
		union{
			struct{
				hword_t age_active;
				hword_t flag;
			};
			word_t age_active_flag;
		};
		struct _flatnode *next;
	} CACHE_ALIGNED;
} _flatnode_t;

typedef struct _flat
{

	struct{
		lkword_t lock;
		hword_t cur_age_active;
		_flatnode_t *head;
	} CACHE_ALIGNED;
	_flatnode_t node[APDS_MAX_THREADS - 1];	/* 凑cache对齐 */

} PAGE_ALIGNED _flat_t;			/* 整体页对齐 */

SAI void flat_enq_req(_flat_t *f, _flatnode_t *node, hword_t age_active)
{
	atm_st(&node->age_active, age_active | FLAT_ACTIVE);
	_flatnode_t *cur_head = atm_ld(&f->head);
	do
	{
		atm_st(&node->next, cur_head);
	} while (!cas_rel(&f->head, &cur_head, node));
}

DECL_COMBINE(flat)

SAI void __flat_init(_flat_t *f)
{
	f->head = (_flatnode_t *)NULL;
	f->lock = 0;
	f->cur_age_active = FLAT_ACTIVE;
}
SAI void *__flat_destruct(_flat_t *s){
	return (void *)s;
}
SAI _flatnode_t *__flat_fetch(_flat_t *f, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo){
	ASSERT(tinfo->tid < APDS_MAX_THREADS - 1);
	_flatnode_t *node = &f->node[tinfo->tid];

	hword_t age_active = atm_ld(&node->age_active);
	apds_movargs(&node->args, args);
	node->func = pfunc;
	/* 标记 */
	atm_strel(&node->flag, TRUE);
	if (!FLAT_IS_ACTIVE(age_active)){
		flat_enq_req(f, node, age_active);
	}
	return node;
}
SAI apdsret_t __flat_wait(_flat_t *f, _flatnode_t *node, INFUN(cmbn_wait, waitpolicy)){
	waitcnt_t cnt = -1;
	word_t age_active_flag;
	hword_t age_active;
	lkword_t locked = 1;
	lkword_t unlocked = 0;
	lkword_t lock;

	/* cmb(); */
	
	lock = atm_ld(&f->lock);
	do {
		age_active_flag = atm_ld(&node->age_active_flag);
		age_active = FLAT_GET_AGE_ACTIVE(age_active_flag);
		while (lock && FLAT_GET_FLAG(age_active_flag) && FLAT_IS_ACTIVE(age_active))
		{
			cnt = waitpolicy->waitf(&f->lock, &locked, cnt);
			lock = atm_ld(&f->lock);
			age_active_flag = atm_ld(&node->age_active_flag);
			age_active = FLAT_GET_AGE_ACTIVE(age_active_flag);
		}
		/* 被其他线程完成 */
		if (!FLAT_GET_FLAG(age_active_flag))
		{
			mp_rmb(); 
			return apds_args_ret(&node->args);
		}
		// else{
/* 			lkword_t tmp = 0;
			if (cas(&f->lock, &tmp, 1))
				break; */
		if (!lock){
			if (cas_acq_s(&f->lock, &lock, 1))
				break;
		}

		// }

		/* 被出队? */
		if (!FLAT_IS_ACTIVE(age_active))
			flat_enq_req(f, node, age_active);
	} while(TRUE);

	/* mp_rmb(); */
	
	if (!FLAT_IS_ACTIVE(atm_ld(&node->age_active)))
		flat_enq_req(f, node, 0);

	
	hword_t cur_age_active = FLAT_INCR_AGE(atm_ld(&f->cur_age_active));
	/* ASSERT(FLAT_IS_ACTIVE(cur_age_active)); */
	word_t cur_age_active_flag = new_word(FALSE, cur_age_active);
	/* ASSERT(FLAT_IS_ACTIVE(FLAT_GET_AGE_ACTIVE(cur_age_active_flag)) && !FLAT_GET_FLAG(cur_age_active_flag)); */
	_flatnode_t *phead = atm_ld(&f->head);
	_flatnode_t *cur = phead;
	_flatnode_t *next;
	/* word_t help_count = 0; */

	do
	{
		next = atm_ld(&cur->next);
		/* APDS_PREFETCH(next); */
		if (LIKELY(atm_ld(&cur->flag)))
		{
			/* cmb(); */
			apds_set_ret(&cur->args, apds_sync_single(cur->func, &cur->args));
			/* 更新age的同时设置flag为完成 */
			atm_strel(&cur->age_active_flag, cur_age_active_flag);
			/* help_count++; */
		}
		
		cur = next;
	} while(cur);

	
	/* cmb(); */
	hword_t cur_age = FLAT_GET_AGE(cur_age_active);
	if (UNLIKELY(0 == (cur_age % FLAT_CLEANUP_FREQUENCY)))
	{
		/* INFO("clean up\n"); */
		/* fprintf(stderr, "clean up %d\n", cur_age); */
		_flatnode_t *prev = phead;
		next = phead->next;
		while ((cur = next))
		{	
			next = atm_ld(&cur->next);
			
			age_active = atm_ld(&cur->age_active);
			if ((age_active + FLAT_CLEANUP_OLD_THRESHOLD) <= (cur_age | FLAT_ACTIVE)){
				// ASSERT(FLAT_IS_ACTIVE(age_active));
				prev->next = next;
				/* 使用读依赖防止重排 */
				/* fprintf(stderr, "cleaned %p\n", cur); */
#ifdef HDEF_WEAK_MEMORY_ORDER
				/* 用依赖读防止重排cur->next的读取被挪到出队之后，next不可能等于cur，因为不可能存在链表成环的情况 */
				if (LIKELY(next != cur))
#endif	
					atm_st(&cur->age_active, cur_age);
				continue;
			}
			prev = cur;
		}
		// 清空
	}
	/* cmb(); */
	atm_st(&f->cur_age_active, cur_age_active);
	atm_strel(&f->lock, 0);
	
	waitpolicy->wakef(&f->lock, &unlocked);
	return apds_args_ret(&node->args);
}

PARAM_NONULL(1) SAI _flat_t *_flat_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	PARAM_USED(nodemgr);
	if (size < sizeof(_flat_t)) return NULL;
	_flat_t *f = (_flat_t *)ptr;
	__flat_init(f);
	return f;
}
PARAM_NONULL(1) SAI void *_flat_destruct(_flat_t *f, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	void *rptr = __flat_destruct(f);
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _flatnode_t *_flat_fetch(_flat_t *f, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	return __flat_fetch(f, pfunc, args, tinfo);
}
PARAM_NONULL(1, 2) SAI apdsret_t _flat_wait(_flat_t *f, _flatnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo, nodemgr);
	apdsret_t ret = __flat_wait(f, node, waitpolicy);
	return ret;
}
